#define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <process.h>
#include "DirectOutput\DirectOutputHelper.h"
#include "DirectOutput\DirectOutput.h"

static void* g_device = 0;
void utt(void *dummy);
void alarm1 (void* arg);
int scrl = 0;
BOOL coord = FALSE;
CRITICAL_SECTION cs;
typedef struct
{
    CDirectOutput* o;
    BOOL s;
    int b;
    int c;
    int p;
}t;

void __stdcall devicechange(void* device, bool added, void* ctxt)
{
	// just grab the first device
	// NOTE: it is good practive to maintain a list of all devices even if you dont use them all
	if (g_device == 0 && added)
		g_device = device;
	else if (!added && g_device == device)
		g_device = 0;
}

void __stdcall softbutton(void* device, DWORD btns, void* ctxt)
{
    HANDLE w;

    if (scrl)
    {
        switch (btns)
        {
            case 0x2:
            if (scrl > 1) scrl--;
            break;
            case 0x4:
            if (scrl < 3) scrl++;
            break;
            default:
            return;
            break;
        }
        w = OpenEvent(EVENT_MODIFY_STATE, TRUE, "eScrl");
        SetEvent(w);
    }
}

void __stdcall pagechange(void* device, DWORD page, bool active, void* ctxt)
{
    HANDLE w;

    switch (page)
    {
        case 1:
        if (!coord)
        {
            coord = TRUE;
            w = OpenEvent(EVENT_MODIFY_STATE, TRUE, "eScrl");
            SetEvent(w);
        } else coord = FALSE;
        break;
        case 2:
        if (!scrl)
        {
            scrl = 1;
            w = OpenEvent(EVENT_MODIFY_STATE, TRUE, "eScrl");
            SetEvent(w);
        } else scrl = 0;
        break;
        default:
        return;
        break;
    }
}

int main()
{
    BOOL loop = TRUE;
    BOOL ct = TRUE;
    HANDLE w[3];
    DWORD ws;
    FILE *f;
    char d[MAX_PATH];
	char s[64]; //buffer for file reading
	wchar_t p[72]; //buffer for formatted string to MFD
	int i, r, x, y, z, sx, sy, sz;
	//char cargo[5][64];

	CDirectOutput output;
	HRESULT hr = output.Initialize(L"Hello World");

	if (SUCCEEDED(hr))
	{
		// find out what devices are attached
		output.RegisterDeviceChangeCallback(devicechange, 0);
		output.Enumerate();

		if (g_device)
		{
			output.RegisterPageChangeCallback(g_device, pagechange, &output);
			output.RegisterSoftButtonChangeCallback(g_device, softbutton, &output);

			// add pages
			output.AddPage(g_device, 0, L"General", TRUE);
			output.AddPage(g_device, 1, L"Coords", FALSE);
			output.AddPage(g_device, 2, L"Cargo", FALSE);

			t *l_f;
            l_f = (t *)malloc(sizeof(t)); l_f->o = &output; l_f->s = FALSE; l_f->b = 19; l_f->p = 500; l_f->c = 1;
            t *i_m;
            i_m = (t *)malloc(sizeof(t)); i_m->o = &output; i_m->s = FALSE; i_m->b = 5; i_m->p = 100; i_m->c = 1;
            t *l_e;
            l_e = (t *)malloc(sizeof(t)); l_e->o = &output; l_e->s = FALSE; l_e->b = 17; l_e->p = 500; l_e->c = 3;
            t *l_s;
            l_s = (t *)malloc(sizeof(t)); l_s->o = &output; l_s->s = FALSE; l_s->b = 17; l_s->p = 500; l_s->c = 1;


            GetCurrentDirectory(MAX_PATH, d);

            //Begin warious event threads whose will wake our app from idle
            w[0] = (HANDLE) _beginthread(utt, 0, NULL); //mingw32 compatibility
		    w[1] = CreateEvent(NULL, FALSE, FALSE, "eScrl");
            w[2] = FindFirstChangeNotification(d, FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE);

            InitializeCriticalSection(&cs);

            strcat(d, "\\savedata.txt");

            //======================
            //Entering main app loop
            //======================
            while (loop)
            {
                EnterCriticalSection(&cs);
                if ((f = fopen (d, "rt")) != NULL)
                {
                    i = 0;
                    ct = TRUE;
                    //Read savedata.txt
                    while (fgets(s, 64, f) != NULL && ct == TRUE)
                    {
                        i++;
                        switch (i)
                        {
                            case 1: //Pilot name
                            output.SetString(g_device, 0, 0, mbstowcs(p, s, strlen(s) - 1), p);
                            break;
                            case 2:
                            sscanf (s, "%i/%i", &x, &y);
                            if (x < 100 && !l_f->s)
                            {
                                l_f->s = TRUE;

                                _beginthread(alarm1, 0, (void *)l_f);
                            } else if (x >= 100 && l_f->s) l_f->s = FALSE;

                            case 3:
                            output.SetString(g_device, 0, 1, swprintf(p, L"Cash: %S", s) - 1, p);
                            break;
                            case 4: //cargo
                            if (scrl)
                            {
                                for (; i <= 8 ; i++)
                                {
                                    if (i >= scrl + 3 && i <= scrl + 5)
                                    {
                                        output.SetString(g_device, 2, i - (scrl + 3), swprintf(p, L"%d: %S", i - 3, s) - 1, p);
                                        //if (i - (scrl + 3) == 2) break;
                                    }
                                    if (fgets(s, 64, f) == NULL) break;
                                }
                            }
                            break;
                            case 8: //9 - 1
                            if (coord)
                            {
                                r = fscanf(f, "%i\n%i\n%i\n%i\n%i\n%i\n", &x, &y, &z, &sx, &sy, &sz);
                                if (r == EOF || r < 6) break;
                                else
                                {
                                    output.SetString(g_device, 1, 0, swprintf(p, L"X:%6d (%5d)", x, sx), p);
                                    output.SetString(g_device, 1, 1, swprintf(p, L"Y:%6d (%5d)", y, sy), p);
                                    output.SetString(g_device, 1, 2, swprintf(p, L"Z:%6d (%5d)", z, sz), p);
                                    i += 6;
                                }
                            }
                            break;
                            case 15: //Sector name
                            output.SetString(g_device, 0, 2, swprintf(p, L"Sec.: %S", s) - 1, p);
                            break;
                            case 16: //energy
                            sscanf(s, "%i", &x);
                            if (x < 25 && !l_e->s)
                            {
                                if (!l_s->s)
                                {
                                    l_e->s = TRUE;
                                    _beginthread(alarm1, 0, (void *)l_e);
                                } else
                                {
                                    l_s->p = 100;
                                }
                            } else if (x >= 25)
                            {
                                if (l_s->s) l_s->p = 500; else if (l_e->s) l_e->s = FALSE;
                            }
                            break;
                            case 21:
                            sscanf(s, "%i", &x);
                            if (x < 25 && !l_s->s)
                            {
                                l_s->s = TRUE;
                                if (l_e->s)
                                {
                                    l_e->s = FALSE;
                                    output.SetLed(g_device, 0, l_e->b+1, 0);
                                    output.SetLed(g_device, 1, l_e->b+1, 0);
                                    output.SetLed(g_device, 2, l_e->b+1, 0);
                                    l_s->p = 100;
                                    _beginthread(alarm1, 0, (void *)l_s);
                                } else
                                {
                                    l_s->p = 500;
                                    _beginthread(alarm1, 0, (void *)l_s);
                                }
                            } else if (x >= 25 && l_s->s) l_s->s = FALSE;
                            break;
                            case 40: //incomming!
                            sscanf(s, "%i", &x);
                            if (x && !i_m->s)
                            {
                                i_m->s = TRUE;

                                _beginthread(alarm1, 0, (void *)i_m);
                            } else if (!x && i_m->s) i_m->s = FALSE;
                            ct = FALSE;
                            break;
                            default:
                            break;
                        }
                    }
                    fclose (f);
                    LeaveCriticalSection(&cs);
                //END reading savedata.txt
                /* variant with keeping cargo contents in memory
                if (scrl)
                {
                    for (j = scrl; j < scrl + 3; j++)
                    {
                        output.SetString(g_device, 2, j - scrl, swprintf(p, L"%d: %S", j, &cargo[j - 1][0]), p);
                    }
                }
                */

                //Enter waiting state for either savedata.txt write or user's input
                ws = WaitForMultipleObjects(3, w, FALSE, INFINITE);
                switch (ws)
                {
                    case WAIT_OBJECT_0:
                    //user terminated. breaking loop
                    loop = FALSE;
                    break;
                    case WAIT_OBJECT_0 + 2:
                    FindNextChangeNotification(w[2]);
                    break;
                    default:
                    break;
                }

            } else
            {
                if (GetLastError() == ERROR_FILE_NOT_FOUND)
                {
                    for (i = 0; i < 3; i++)
                    {
                        output.SetString(g_device, i, 0, 16,L"Waiting for game");
                        output.SetString(g_device, i, 1, 16,L"    ........    ");
                        output.SetString(g_device, i, 2, 16,L"      ....      ");
                    }
                    ws = WaitForMultipleObjects(3, w, FALSE, INFINITE);
                    if (ws == WAIT_OBJECT_0) loop = FALSE; //user terminated. breaking loop
                } else
                {
                    //actually should handle other i/o errors here. oh well...
                    Sleep(50L);
                }
            }
            }
            //================================
            //End of main loop. Finishing app.
            //================================

            DeleteCriticalSection(&cs);
			// remove page 0
			hr = output.RemovePage(g_device, 0);
			FindCloseChangeNotification(w[2]);
			CloseHandle(w[1]);
			// page 1 will be removed when DirectOutput_Deinitialize is called

		} else
		{
			printf("No DirectOutput devices found, press any key to quit\n");
			getchar();
		}

		output.Deinitialize();
	} else
	{
		printf("Failed to initialize DirectOutput, press any key to quit\n");
		getchar();
	}

	return 0;
}

void utt (void *dummy)
{
    // wait for the user to quit
	printf("Press any key to quit\n");
	getchar();
}

void alarm1 (void* arg)
{
    t *p = (t*)arg;
    while (p->s)
    {
        EnterCriticalSection(&cs);
        if (p->c == 3)
        {
        p->o->SetLed(g_device, 0, p->b, 1);
        p->o->SetLed(g_device, 1, p->b, 1);
        p->o->SetLed(g_device, 2, p->b, 1);
        p->o->SetLed(g_device, 0, p->b + 1, 1);
        p->o->SetLed(g_device, 1, p->b + 1, 1);
        p->o->SetLed(g_device, 2, p->b + 1, 1);
        } else
        {
        p->o->SetLed(g_device, 0, p->b, 1);
        p->o->SetLed(g_device, 1, p->b, 1);
        p->o->SetLed(g_device, 2, p->b, 1);
        }
        LeaveCriticalSection(&cs);
        Sleep(p->p);
        EnterCriticalSection(&cs);
        if (p->c == 3)
        {
        p->o->SetLed(g_device, 0, p->b, 0);
        p->o->SetLed(g_device, 1, p->b, 0);
        p->o->SetLed(g_device, 2, p->b, 0);
        p->o->SetLed(g_device, 0, p->b + 1, 0);
        p->o->SetLed(g_device, 1, p->b + 1, 0);
        p->o->SetLed(g_device, 2, p->b + 1, 0);
        } else
        {
        p->o->SetLed(g_device, 0, p->b, 0);
        p->o->SetLed(g_device, 1, p->b, 0);
        p->o->SetLed(g_device, 2, p->b, 0);
        }
        LeaveCriticalSection(&cs);
        Sleep(p->p);
    }
    if (p->b == 19)
    {
        EnterCriticalSection(&cs);
        p->o->SetLed(g_device, 0, 19, 1);
        p->o->SetLed(g_device, 1, 19, 1);
        p->o->SetLed(g_device, 2, 19, 1);
        LeaveCriticalSection(&cs);
    }
}

